/* eslint indent: ["off"] */
/* eslint space-before-function-paren: ["off"] */
/* eslint indent: ["off"] */
/* eslint space-before-function-paren: ["off"] */

/*
3. 使用多个号码预测：
问题：
你当前的模型每次只预测一个数字，然而你想要的是 24 个不同的号码。ARIMA 模型本身通常是在 单一序列 里进行预测的，而你期望的结果是从多个候选号码中选择，并保证它们不重复。
解决方法：
创建多个模型：如果你需要从 1-49 之间的多个号码中选择，你可以考虑对所有 49 个数字分别训练 ARIMA 模型。通过独立的训练模型，对每个号码预测 24 次，然后根据预测的结果选择高概率的号码。
替代算法：ARIMA 可能不是最适合处理多个号码预测的算法。你可以尝试其他 多目标预测方法，比如 随机森林、XGBoost 等机器学习算法，这些算法可以处理多个输出目标并给出更多的多样化结果。


解决方法：
创建多个模型：如果你需要从 1-49 之间的多个号码中选择，你可以考虑对所有 49 个数字分别训练 ARIMA 模型。通过独立的训练模型，对每个号码预测 24 次，然后根据预测的结果选择高概率的号码。
替代算法：ARIMA 可能不是最适合处理多个号码预测的算法。你可以尝试其他 多目标预测方法，比如 随机森林、XGBoost 等机器学习算法，这些算法可以处理多个输出目标并给出更多的多样化结果。


解决方法：
尝试使用 LSTM 或 RNN 等基于神经网络的时间序列预测模型，这些模型可以捕捉更加复杂的时间序列模式。
使用 XGBoost 或 随机森林 等算法进行多目标回归，预测不同号码的概率，并进行排序后选取。
*/

// 引入 ARIMA 库
const Arima = require('arima')

// 引入路径模块
const path = require('path')

// 获取项目根目录
const ROOT_DIR = path.join(__dirname, '..')
// helpers 目录
const HELPERS_DIR = path.join(ROOT_DIR, 'helpers')

// 读取所有数据
let { allValues } = require(path.join(HELPERS_DIR, 'readAllValues.js'))

// 100 - 10 - 1
// 101 - 10 - 1
// 102 - 24 - 1
// 103 - 24 - 0   36ok
// 104 - 24 - 1
// 105 - 34 - 0   34ok
// 106 - 16 - 0   16ok

const qishu = 107
const zongshu = 39

// 假设前100期是有效历史数据
const first100 = allValues.slice(0, qishu) // 获取前100期数据

// 获取历史开奖号码（只提取number）
const historyNumbers = first100.map((item) => parseInt(item.number, 10))

// 存储每个号码的预测结果
let allPredictions = []

for (let number = 1; number <= 49; number++) {
  try {
    // 为每个号码创建 ARIMA 模型
    const arimaModel = new Arima({
      p: 5, // 自回归项数
      d: 1, // 差分次数
      q: 2 // 移动平均项数
    })

    // 获取该号码在历史数据中的出现情况
    const historyForNumber = historyNumbers.map((num) => (num === number ? 1 : 0))

    // 训练 ARIMA 模型
    arimaModel.train(historyForNumber)

    // 进行预测
    const forecast = arimaModel.predict(24).flat() // 预测24个数据点

    // 将预测结果四舍五入，并保证在 1 到 49 之间
    const roundedPredictions = forecast.map((num) => {
      let rounded = Math.round(num) // 四舍五入
      // 保证在 1 到 49 之间
      if (rounded < 1) return 1
      if (rounded > 49) return 49
      return rounded
    })

    // 存储该号码的预测结果
    allPredictions.push({
      number,
      predictions: roundedPredictions
    })
  } catch (error) {
    console.error(`训练模型并预测号码 ${number} 失败:`, error)
  }
}

// console.log('allPredictions', allPredictions)

// 步骤2：根据预测结果选择最可能出现的号码
const allForecasts = allPredictions.map((item) => ({
  number: item.number,
  meanPrediction: item.predictions.reduce((sum, value) => sum + value, 0) / item.predictions.length // 计算预测的均值
}))

// 根据均值选择最有可能的前 10 个号码
const sortedForecasts = allForecasts.sort((a, b) => b.meanPrediction - a.meanPrediction)
const topPredictions = sortedForecasts.slice(0, zongshu) // 选择前 10 个预测号码

// 输出最终预测结果
console.log(
  `预测的最有可能的 ${zongshu} 个号码:`,
  topPredictions.map((item) => item.number)
)
console.log(allValues[qishu])
